package org.rcgwt.client.ui.fields;

import java.util.Stack;

import org.rcgwt.client.theme.CssStyle;
import org.rcgwt.client.ui.fields.theme.ITheme;
import org.rcgwt.client.ui.validator.IChecker;
import org.rcgwt.client.ui.validator.IClientValidatorContext;
import org.rcgwt.client.ui.validator.IConverter;
import org.rcgwt.client.ui.validator.IFilter;
import org.rcgwt.client.ui.validator.IFormatter;
import org.rcgwt.client.ui.validator.IMasker;
import org.rcgwt.client.ui.validator.ITranslator;
import org.rcgwt.client.ui.validator.ValidatorRepositoryFactory;
import org.rcgwt.client.ui.validator.events.MessageEvent;
import org.rcgwt.client.ui.validator.messages.IMessage;
import org.rcgwt.client.ui.validator.messages.IMessageManager;
import org.rcgwt.client.ui.validator.messages.IValidationContext;
import org.rcgwt.client.ui.validator.messages.Message;
import org.rcgwt.client.ui.validator.messages.Severity;
import org.rcgwt.client.ui.validator.messages.ValidatorMessagesProvider;
import org.rcgwt.client.util.KeyUtil;
import org.rcgwt.client.util.KeyUtilFirefox;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.resources.client.ResourceException;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;

/**
 * 
 * <b>A validated text field.</b>
 * 
 * <b>Validation Mechanism</b>
 * <p>
 * This label is managed by a validator that contains some elements, each step
 * must be validated to reach the next step :
 * <ul>
 * <li>1st : A filter that filters invalid characters</li>
 * <li>2nd : A translator that translates valid character</li>
 * <li>3rd : A masker that guiding the entry in the field</li>
 * <li>4th : A checker that checks the field value and return a feedback to the
 * user (a customizable feedback) if the entry isn't valid</li>
 * <li>5th : A formatter that formats the content of the field
 * </ul>
 * 
 * <p>
 * There are 2 validation times :
 * <ul>
 * <li>When the user enters a character : filtering & translating & masking</li>
 * <li>When the user dropped the focus : checking and formating</li>
 * </ul>
 * <br>
 * 
 * <b>ClientValidators list (name & role)</b><br>
 * 
 * The ClientValidator associated, can be configurable in a XML configuration
 * file. <br>
 * <br>
 * <br>
 * Some defaults client validators are available :
 * <ul>
 * <li>noBlank : refuses blank characters</li>
 * <li>skipBlank : eliminates blank at the begin and at the end of the entry</li>
 * <li>removeAccents : removes accents from the entry</li>
 * <li>scientificChecked : filters and checks a scientific form number</li>
 * <li>scientificFormatted : filters and transforms a classic form number in
 * scientific form number</li>
 * <li>money : checks and transforms an money form entry</li>
 * <li>number : filters, checks and transforms a number</li>
 * <li>hour : filters, checks an hour</li>
 * <li>date : filters, checks and formats a date</li>
 * <li>year : filters and checks a year</li>
 * <li>alpha_fr : filters alphabetic characters from French language</li>
 * <li>alpha_fr_upper : filters alphabetic characters from French language and
 * translate on the fly to upper case character</li>
 * <li>alpha_fr_lower : filters alphabetic characters from French language and
 * translate on the fly to lower case character</li>
 * <li>alphaDigit_fr : filters alphanumeric characters from French language</li>
 * <li>alphaDigit_fr_upper : filters alphanumeric characters from French
 * language and translate on the fly to upper case character</li>
 * <li>alphaDigit_fr_lower : filters alphanumeric characters from French
 * language and translate on the fly to lower case character</li>
 * <li>name : filters and translates a name</li>
 * <li>civility : filters and translates civility</li>
 * <li>email : filters, translates and checks email</li>
 * <li>insee : filters, translates, checks, and formats insee number form</li>
 * <li>creditCard : filters, checks, and formats credit card number</li>
 * </ul>
 * </p>
 * <br>
 * Each client validator is parameterizable. Parameter structure is normalized
 * like : <i>[validator name].[parameter name]</i> and have a specific value. <br>
 * Parameters can be of 2 different types :
 * <ul>
 * <li>Global : A global parameter is applied to all elements of the Client
 * Validator</li>
 * <li>Specific : A specific parameter is as its name indicates, a parameter
 * specific to a Client Validator element.</li>
 * </ul>
 * 
 * <b>Uses example</b><br>
 * <p>
 * To set a ClientValidator to the field, you only have to call the method
 * ValidatedTextField.setValidator(ClientValidatorName). The following code show
 * how sets the ClientValidator named "creditCard" to the field : <br>
 * 
 * <pre>
 * ValidatedTextField field = new ValidatedTextField();
 * try {
 * 	field.setValidator(&quot;creditCard&quot;);
 * } catch (ResourceException e) {
 * 	e.printStackTrace();
 * }
 * </pre>
 * 
 * </p>
 * 
 * If you want to extend the Client Validator configuration you can specify a
 * XML configuration file path in the GWT module configuration file (*.gwt.xml)
 * like this : <br>
 * <br>
 * 
 * <code> &lt;extend-configuration-property
 * name="org.rcgwt.validator.ConfigurationFile"
 * value=".../configuration.xml;2"/&gt; </code>
 * 
 * <p>
 * <b>Configuration priority</b><br>
 * The number placed at the end of the path corresponds to the priority of the
 * configuration file's ClientValidators if some of them are already defined in
 * another configuration file. Thus ClientValidators defined in the
 * configuration file which has the higher priority will be the selected one.
 *</p>
 * 
 * <p>
 * <b>Configuration Syntax</b><br>
 * A ClientValidator is defined in the configuration file like this:
 * 
 * <pre>
 * &lt;ClientValidator name='date' extends='insee_inherits'&gt;
 * 		&lt;filter class='org.rcgwt.client.ui.validator.impl.DateFilter'&gt;
 * 			&lt;parameter name='date.sepSign' value='/' /&gt;
 * 		&lt;/filter&gt;
 * 		&lt;checker class='org.rcgwt.client.ui.validator.impl.DateChecker'&gt;
 * 			&lt;parameter name='date.begin' value='1950' /&gt;
 * 			&lt;parameter name='date.end' value='2050' /&gt;
 * 			&lt;parameter name='date.sepSign' value='/' /&gt;
 * 			&lt;parameter name='date.format' value='us' /&gt;
 * 		&lt;/checker&gt;
 * 		&lt;formatter class='org.rcgwt.client.ui.validator.impl.DateFormatter'&gt;
 * 			&lt;parameter name='date.sepSign' value='/' /&gt;
 * 			&lt;parameter name='date.format' value='us' /&gt;
 * 		&lt;/formatter&gt;
 * 		&lt;converter class='org.rcgwt.client.ui.validator.impl.DateConverter'&gt;
 * 			&lt;parameter name='date.format' value='us' /&gt;
 * 		&lt;/converter&gt;
 * 	&lt;/ClientValidator&gt;
 * </pre>
 * 
 * <br>
 * It is characterized by a name, a super validator (the validation elements and
 * parameters will be inherited), parameters, and some validator elements (one
 * for each type). The name value will be used in the ClientValidator call.
 * </p>
 * 
 * <p>
 * <b>Message oriented structure</b><br>
 * This component can be integrated in a message structure. Thus through a
 * MessageManager component, the ValidatedTextField can send a
 * (error|warning|information) message to another component. In this case the
 * validation step is accomplished when the method valid() is called.
 * </p>
 * 
 * @version 1.0
 * @author Elian ORIOU (last modifications by $Author)
 * 
 */

public class ValidatedTextField<T> extends Composite {

	// The Theme applied to the field
	private ITheme theme;

	// Controls
	private HorizontalPanel rootPanel;
	private TextBox text;

	/* State booleans */
	// Password Field behavior
	private boolean isPassword;
	// If the entry is checked (after filtering)
	private boolean isValid = false;
	// If the entry is checked (after checking)
	private boolean isChecked = false;
	// If the entry is formatted (after format)
	private boolean isFormatted = false;
	// The default value
	private boolean isDefaultBehavior = false;
	// If the second time validation (check & format) is effected after blur or
	// submit (in the case of a MessageManager)
	private boolean validAfterSubmit = false;
	// If the focus is taken
	private boolean isOnFocus = false;
	// The field cannot be empty
	private boolean isRequired = false;

	// Validator Objects
	private IFilter filter;
	private ITranslator translator;
	private IChecker checker;
	private IFormatter formatter;
	private IConverter<T> converter;
	private IMasker masker;
	private IMessageManager manager;

	// The internal value
	private String internalValue = "";

	// The ClientValidator Context
	private IClientValidatorContext context;

	// KeyUtil Tool
	private KeyUtil keyutil;

	/**
	 * The no-parameter constructor
	 */

	public ValidatedTextField() {
		this(false);
	}

	/**
	 * Constructs a ValidatedTextField with a Password field behavior
	 * 
	 * @param isPass
	 *            The TextField password behavior
	 */

	public ValidatedTextField(boolean isPass) {
		isPassword = isPass;
		this.render();

		sinkEvents(Event.ONPASTE);
		this.initWidget(rootPanel);
	}

	/**
	 * Component rendering method
	 */
	protected void render() {
		/* CSS Styles */
		theme = ITheme.STANDARD;
		/* Panels */
		rootPanel = new HorizontalPanel();
		rootPanel.setStyleName(theme.getValidatedTextFieldMainStyle()
				.getClassName());
		context = new ClientValidatorContextImpl();

		/* Password field behavior */
		if (!isPassword) {
			text = new TextBox();
		} else {
			text = new PasswordTextBox();
		}

		text.setStyleName(theme.getDefaultTextStyle().getClassName());

		// /////////////////////////
		// //MANAGE KEYBOARD EVENTS
		// /////////////////////////

		/* To accept or not character (Filter) */
		/* To translate on the fly the content of the field (Translator) */
		text.addKeyPressHandler(new KeyPressHandler() {
			@Override
			public void onKeyPress(KeyPressEvent event) {
				keyutil = GWT.create(KeyUtilFirefox.class);
				char currentChar = keyutil.getCharCode(event.getNativeEvent());
				filter(currentChar);
				translate(currentChar);
				if (translator == null && masker!= null && masker.getPattern() != null
						&& isValid == true) {
					mask(currentChar);
				}
			}
		});
		/* To manage erasing action when a masker is defined */
		text.addKeyDownHandler(new KeyDownHandler() {
			@Override
			public void onKeyDown(KeyDownEvent event) {
				int cursorPos = text.getCursorPos();
				if (masker!=null && masker.getPattern() != null) {
					/* If the DELETE KEY is pressed */
					if (event.getNativeKeyCode() == KeyCodes.KEY_DELETE) {
						text.cancelKey();
						/* If the user has selected a text */
						if (text.getSelectionLength() != 0) {
							/* Only the editable text will be deleted */
							if (text.getSelectionLength() == text.getValue()
									.length()) {
								text.setValue("");
								return;
							}
							deleteZone(text.getCursorPos(), text.getCursorPos()
									+ text.getSelectionLength());
							text.setCursorPos(cursorPos);
							return;
						}
						/*
						 * Deletes editable text at the cursor position with a
						 * left-side character shifting
						 */
						if (masker.isEditablePosition(cursorPos) == true) {
							shiftCharacterToLeft();
							text.setCursorPos(cursorPos);
						} else {
							/*
							 * If the text at cursor position is not editable
							 * the cursor position must be placed at the next
							 * editable position
							 */
							text.setCursorPos(masker
									.getNextEditablePosition(cursorPos));
						}
						return;
					}
					/* If the BACKSPACE KEY is pressed */
					if (event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE
							&& cursorPos >= 1) {
						text.cancelKey();
						if (text.getSelectionLength() != 0) {
							deleteZone(text.getCursorPos(), text.getCursorPos()
									+ text.getSelectionLength());
							text.setCursorPos(cursorPos);
							return;
						}
						if (masker.isEditablePosition(cursorPos - 1) == true) {
							text.setValue(replaceText(text.getText(), String
									.valueOf(masker.getReplacedBy()), text
									.getCursorPos() - 1, text.getCursorPos()));
							text.setCursorPos(cursorPos - 1);
						} else if (masker.isEditablePosition(cursorPos) == true) {
							text.setCursorPos(cursorPos - 1);
						} else {
							int pep = masker
									.getPreviousEditablePosition(cursorPos);
							text.setCursorPos(pep + 1);
						}
						return;
					}
				}
			}
		});
		/*
		 * To check and format the field value when focus off (Checker,
		 * Formatter)
		 */
		text.addBlurHandler(new BlurHandler() {
			// BlurEvent Manager
			@Override
			public void onBlur(BlurEvent event) {
				isOnFocus = false;
				if (getTextualValue().isEmpty() == false
						&& validAfterSubmit == false) {
					internalValue = getTextualValue();
					check();
					format();
				}
			}
		});
		/* To update the field with the internal value on focus */
		text.addFocusHandler(new FocusHandler() {
			@Override
			public void onFocus(FocusEvent event) {
				isOnFocus = true;
				text.setStyleName(theme.getOtherTextStyle().getClassName());
				isFormatted = false;
				if (isDefaultBehavior == true) {
					isDefaultBehavior = false;
					text.setText("");
				}
				if (internalValue.isEmpty() == false) {
					text.setValue(internalValue);
				} else if (masker != null) {
					text.setText(masker.getPattern());
				}
			}
		});
		/* To split internal value between displayed value */
		text.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				internalValue = event.getValue();
			}
		});

		// ///////////////////////////////
		// ADDING ELEMENT TO ROOT PANEL
		// ///////////////////////////////

		rootPanel.add(text);
	}

	/**
	 * Captures browser events
	 */

	@Override
	public void onBrowserEvent(Event event) {
		super.onBrowserEvent(event);

		switch (event.getTypeInt()) {
		case Event.ONPASTE:
			event.preventDefault();
			if (masker.getPattern() != null) {
				pasteWithMask(getPastedText(event));
			} else {
				pasteNormally(getPastedText(event));
			}
			break;
		}
	}

	/**
	 * Shift characters to the left (to delete a character)
	 */

	private void shiftCharacterToLeft() {
		String t = text.getText();
		String s = t;
		for (int i = text.getCursorPos(); i < t.length() - 1; i++) {
			if (masker.isEditablePosition(i) == true) {
				if (masker.isEditablePosition(i + 1) == true) {
					/*
					 * 1st case : if the next character is on editable position,
					 * it will be replaced by the next character
					 */
					s = replaceText(s, String.valueOf(s.charAt(i + 1)), i,
							i + 1);
				} else if (masker.getNextEditablePosition(i) != -1) {
					/*
					 * 2nd case : if the next character is not on editable
					 * position it will be replaced by the character placed on
					 * the next editable position
					 */

					int previous = i - 1;
					i = masker.getNextEditablePosition(i);
					s = replaceText(s, String.valueOf(s.charAt(i)), previous,
							previous + 1);
				} else {
					/*
					 * 3rd case : Otherwise the character is replaced by the
					 * default replacedBy character
					 */
					s = replaceText(s, String.valueOf(masker.getReplacedBy()),
							i, i + 1);
				}
			}
		}
		text.setText(s);
	}

	/**
	 * Delete a textual zone (when the user make a selection)
	 * 
	 * @param begin
	 *            the begin selection index
	 * @param end
	 *            the end selection index
	 */

	private void deleteZone(int begin, int end) {
		String s = text.getText();
		for (int i = begin; i < end; i++) {
			if (masker.isEditablePosition(i) == true) {
				/*
				 * If the character placed in the range (limited by begin & end)
				 * is placed on an editable position, it will be deleted
				 */
				s = replaceText(s, String.valueOf(masker.getReplacedBy()), i,
						i + 1);
			}
		}
		text.setText(s);
	}

	/**
	 * Returns the clipboard content (it doesn't works for Opera)
	 * 
	 * @param event
	 *            The OnPasteEvent
	 * @return
	 */

	private static native String getPastedText(Event event)
	/*-{
		var text = "";

		if (event.clipboardData) {// WebKit/Chrome/Safari
			try{
				text = event.clipboardData.getData("Text");
				return text;
			}catch (e){
				//PROBLEM
			}
		}
		if ($wnd.clipboardData){ //IE
			try{
				text = $wnd.clipboardData.getData("Text");
				return text;
			}catch (e) {
				//PROBLEM
			}
		}
		try {
			if (netscape.security.PrivilegeManager.enablePrivilege){
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			}else{
				return "";
			}
		}catch (ex){
			return "";
		}

		var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
		if (!clip) return "";

		var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
		if (!trans) return "";

		trans.addDataFlavor("text/unicode");
		clip.getData(trans, clip.kGlobalClipboard);

		var str = new Object();
		var strLength = new Object();

		trans.getTransferData("text/unicode", str, strLength);
		if (str) str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
		if (str) text = str.data.substring(0, strLength.value / 2);

		return text;
	}-*/;

	/**
	 * Filtered and translated paste method
	 * 
	 * @param s
	 * @return
	 */

	private void pasteNormally(String pastedText) {
		for (int i = 0; i < pastedText.length(); i++) {
			char toPasteChar = pastedText.charAt(i);
			filter(toPasteChar);
			if (isValid) {
				addText(String.valueOf(toPasteChar));
			}
		}
	}

	/**
	 * Supports paste on the textfield when a masker is defined (otherwise it is
	 * the pasteNormally method called)
	 * 
	 * @param s
	 * @return
	 */

	public boolean pasteWithMask(String s) {
		int i = masker.getFirstEditablePosition();
		int j = 0;
		while (i < masker.getPattern().length() && j < s.length()) {
			/* Character by character comparison */
			char toPasteChar = s.charAt(j);
			filter(toPasteChar);
			if (isValid == true) {
				if (masker.isEditablePosition(i)
						|| masker.getPattern().charAt(i) == toPasteChar) {
					text.setText(replaceText(text.getText(), String
							.valueOf(toPasteChar), i, i + 1));
				} else if (masker.getNextEditablePosition(i) != -1) {
					i = masker.getNextEditablePosition(i);
					text.setText(replaceText(text.getText(), String
							.valueOf(toPasteChar), i, i + 1));
				}
			}
			i++;
			j++;
		}
		return false;
	}

	/**
	 * Loads the configuration
	 * 
	 * @param configName
	 *            The configuration name
	 * @throws ResourceException
	 *             If the resource doesn't exists
	 */

	public void setValidator(String configName) throws ResourceException {

		ValidatorRepositoryFactory vr = ValidatorRepositoryFactory.INSTANCE;
		vr.applyValidator(this, configName);
	}

	/**
	 * Filters the field value
	 */

	private void filter(char c) {
		if (filter == null) {
			isValid = true;
			return;
		}
		text.cancelKey();
		if (filter.filter(context, c)) {
			isValid = true;
			if (translator == null && masker.getPattern() == null) {
				/*
				 * If a translator or a masker are defined, the text adding is
				 * delegated at the translate or at the mask method
				 */
				addText(String.valueOf(c));
			}
		} else {
			isValid = false;
		}
	}

	/**
	 * Translates the field value For the translation, the keyboard event is
	 * caught, but the character is invalidate. That why the insertion &
	 * modification behaviors must be redefined.
	 */

	private char translate(char c) {

		if (translator == null || isValid == false) {
			return (char) -1;
		}
		text.cancelKey();
		char ch = translator.translate(context, c);
		if (masker.getPattern() == null) {
			/*
			 * If a masker are defined, the text adding is delegated at the mask
			 * method
			 */
			addText(String.valueOf(ch));
			return ch;
		} else {
			mask(ch);
			return ch;
		}
	}

	/**
	 * Add text to the field at the user cursor position
	 * 
	 * @param t
	 *            The text to add
	 */

	public void addText(String t) {
		/* If the user hasn't selected a text */
		if (text.getSelectionLength() == 0) {
			int cursorPos = text.getCursorPos();
			text.setValue(addString(getTextualValue(), t, cursorPos));
			text.setCursorPos(cursorPos + 1);
		}
		/* If the user has selected a text to replace */
		else {
			int beginPos = text.getCursorPos();
			int endPos = beginPos + text.getSelectionLength();
			text.setValue(replaceText(getTextualValue(), String.valueOf(t),
					beginPos, endPos));
			text.setCursorPos(beginPos + 1);
		}
	}

	/**
	 * Insert string in another string at the specified position
	 * 
	 * @param src
	 *            The string to modify
	 * @param tta
	 *            The string to add
	 * @param pos
	 *            The position where adding the string
	 * @return The string modified
	 */

	private String addString(String src, String tta, int pos) {
		/*
		 * Split string in 2 parts (------leftPart------TEXT TO ADD (at
		 * pos)-------rightPart-------)
		 */
		if (pos <= src.length()) {
			String leftPart = src.substring(0, pos);
			String rightPart = src.substring(pos);
			return leftPart + tta + rightPart;
		}
		return src + tta;
	}

	/**
	 * Replaces text in a string at he specified positions <i>begin</i> &
	 * <i>end</i>
	 * 
	 * @param src
	 *            The text to modify
	 * @param ttr
	 *            The text to replace
	 * @param beginPos
	 *            The replacement begin index
	 * @param endPos
	 *            The replacement end index
	 * @return The text modified
	 */

	private String replaceText(String src, String ttr, int beginPos, int endPos) {
		/*
		 * Split string in 2 parts (------leftPart------SELECTION ZONE(beginPos
		 * --> endPos)-------rightPart-------)
		 */
		if (endPos <= src.length()) {
			String leftPart = src.substring(0, beginPos);
			String rightPart = src.substring(endPos, src.length());
			return leftPart + ttr + rightPart;
		}
		return src + ttr;
	}

	/**
	 * If a masker is defined
	 * 
	 * @param currentChar
	 */

	private void mask(char c) {
		int nep = masker.getNextEditablePosition(text.getCursorPos());
		if (masker.isEditablePosition(text.getCursorPos()) == false) {
			/*
			 * 1st case : A character can be only added on a editable position,
			 * so the cursor position must be placed on the next editable
			 * position
			 */
			if (nep != -1) {
				text.setCursorPos(nep);
				if (masker.isEditablePosition(nep)) {
					text.setValue(replaceText(text.getText(),
							String.valueOf(c), text.getCursorPos(), text
									.getCursorPos() + 1));
					text.setCursorPos(nep + 1);
				}
			}
		} else {
			/*
			 * 2nd case : The character is on an editable position, so it will
			 * be added at the current cursor position
			 */
			int cursorPos = text.getCursorPos();
			text.setValue(replaceText(text.getText(), String.valueOf(c), text
					.getCursorPos(), text.getCursorPos() + 1));
			text.setCursorPos(cursorPos + 1);
		}
	}

	/**
	 * Checks the field value
	 */

	private void check() {
		if (checker == null) {
			isChecked = true;
			return;
		}
		resetErrorFeedBack();
		/* If the field has an required behavior */
		if (getTextualValue().isEmpty() == true && isRequired == true) {
			context.addMessage(context.getMessagesProvider()
					.RequiredFieldErrorShort(), context.getMessagesProvider()
					.RequiredFieldError(), Severity.ERROR);
		}
		/*
		 * If the text field value is not accepted by the checker the error logo
		 * is attached to the field
		 */
		if (isFormatted == true) {
			return;
		}
		if (!checker.check(context, getTextualValue())) {
			isChecked = false;
		} else {
			isChecked = true;
		}
	}

	/**
	 * Formats the field value
	 */

	private void format() {
		/* If the entry is already formated */
		if (isFormatted == true) {
			return;
		}
		if (formatter != null) {
			if (checker != null) {
				if (isChecked == true) {
					text.setValue(formatter.format(context, getTextualValue()));
					isFormatted = true;
					return;
				}
			} else {
				text.setValue(formatter.format(context, getTextualValue()));
				isFormatted = true;
				return;
			}
		}
		isFormatted = false;
	}

	/**
	 * Sets the filter to <i>f</i>
	 * 
	 * @param f
	 */

	public void setFilter(IFilter f) {
		this.filter = f;
	}

	/**
	 * Sets the translator to <i>t</i>
	 * 
	 * @param t
	 */

	public void setTranslator(ITranslator t) {
		this.translator = t;
	}

	/**
	 * Sets the checker to <i>c</i>
	 * 
	 * @param c
	 */

	public void setChecker(IChecker c) {
		this.checker = c;
	}

	/**
	 * Sets the formatter to <i>f</i>
	 * 
	 * @param f
	 */

	public void setFormatter(IFormatter f) {
		this.formatter = f;
	}

	/**
	 * Sets the converter to <i>c</i>
	 * 
	 * @param c
	 */

	public void setConverter(IConverter<T> c) {
		this.converter = c;
	}

	/**
	 * Sets the masker to <i>masker</i>
	 * 
	 * @param masker
	 */

	public void setMasker(IMasker masker) {
		this.masker = masker;
	}

	/**
	 * Sets the message manager to <i>manager</i>
	 * 
	 * @param manager
	 *            The assigned manager
	 */

	public void setManager(IMessageManager manager) {
		this.manager = manager;
	}

	/**
	 * Gets the message manager
	 * 
	 * @return The message manager
	 */

	public IMessageManager getManager() {
		return manager;
	}

	/**
	 * Valid the current field value
	 * 
	 * @param c
	 *            The validation context
	 */

	public void valid(IValidationContext c) {
		resetErrorFeedBack();
		if (getTextualValue().isEmpty() == true) {
			/* If the field is set as required, the field value must not be null */
			if (isRequired == true) {
				context.addMessage(context.getMessagesProvider()
						.RequiredFieldErrorShort(), context
						.getMessagesProvider().RequiredFieldError(),
						Severity.ERROR);
			}
			return;
		}
		if (isFormatted == false) {
			/*
			 * Set the internal value of the field at the current displayed
			 * field value
			 */
			internalValue = getTextualValue();
		}
		check();
		format();
	}

	/**
	 * Resets the field Value
	 */

	public void resetValue() {
		text.setText("");
		internalValue = "";
		resetErrorFeedBack();
	}

	/**
	 * Returns the checker state (if the entry is accepted)
	 * 
	 * @return the checker state
	 */

	public boolean getCheckState() {
		return isChecked;
	}

	/**
	 * Reset the error feedback
	 */

	private void resetErrorFeedBack() {
		rootPanel.setStyleName(theme.getValidatedTextFieldMainStyle()
				.getClassName());
	}

	/**
	 * Displays the error feedback to user
	 */

	public void showErrorFeedBack() {
		rootPanel.setStyleName(getErrorStyle().getClassName());
	}

	/**
	 * Returns the error state associated style
	 * 
	 * @return the error state associated style
	 */

	protected CssStyle getErrorStyle() {
		return theme.getErrorStyle();
	}

	/**
	 * Returns the internal value
	 * 
	 * @return the internal value
	 */

	@SuppressWarnings("unused")
	private String getInternalValue() {
		return internalValue;
	}

	/**
	 * Return the field value as an object (when a converter is defined)
	 * 
	 * @return the field value
	 */

	public T getValue() {
		/*
		 * If a default text is entered or if the entry is not validated, or if
		 * the user is entering a value
		 */
		if (isDefaultBehavior == true || isChecked == false
				|| isOnFocus == true) {
			return null;
		}
		if (converter != null) {
			if (isFormatted == true) {
				return converter.getAsObject(context, this, internalValue);
			} else {
				return converter.getAsObject(context, this, text.getValue());
			}
		}
		return null;
	}

	/**
	 * Returns the textual value of the field
	 * 
	 * @return The textual value
	 */

	public String getTextualValue() {
		if (isDefaultBehavior == true) {
			return "";
		}
		return text.getValue();
	}

	/**
	 * Sets the field value and the internal value to <i>t</i>
	 * 
	 * @param t
	 */

	public void setValue(String t) {
		for (int i = 0; i < t.length(); i++) {
			DomEvent.fireNativeEvent(Document.get().createKeyPressEvent(true,
					true, true, true, 0, t.charAt(i)), text);
		}
		if (filter == null && translator == null) {
			text.setValue(t);
		}
		DomEvent.fireNativeEvent(Document.get().createBlurEvent(), text);
		DomEvent.fireNativeEvent(Document.get().createChangeEvent(), this);
	}

	/**
	 * Sets the value to display, before the user enters a value
	 * 
	 * @param dt
	 *            The default value to display
	 */

	public void setDefaultText(String dt) {
		if (isOnFocus == true) {
			return;
		}
		isDefaultBehavior = true;
		text.setStyleName(theme.getDefaultTextStyle().getClassName());
		text.setText(dt);
	}

	/**
	 * Enables/Disables the validation at blur time
	 * 
	 * @param t
	 */

	public void setValidAfterSubmit(boolean t) {
		validAfterSubmit = t;
	}

	/**
	 * If the validation is effected at blur time or at submission
	 * 
	 * @return true if the validation behavior is sets at the submission time,
	 *         false otherwise
	 */

	public boolean isValidAfterSubmit() {
		return validAfterSubmit;
	}

	/**
	 * Returns true if the field is required, false otherwise
	 * 
	 * @return the required behavior
	 */

	public boolean isRequired() {
		return isRequired;
	}

	/**
	 * Sets the required behavior to the field
	 * 
	 * @param isRequired
	 */

	public void setRequired(boolean isRequired) {
		this.isRequired = isRequired;
	}

	/**
	 * Transmits message to the MessageManager
	 * 
	 * @param e
	 *            The message
	 */

	public void manageMessage(Message e) {
		if (manager == null) {
			return;
		}
		MessageEvent me = new MessageEvent(e, this);
		manager.addMessageEvent(me);
	}

	/**
	 * Returns the text component
	 * 
	 * @return the text component
	 */

	public TextBox getTextComponent() {
		return text;
	}

	/**
	 * Sets the field theme
	 * 
	 * @param theme
	 */

	public void setTheme(ITheme theme) {
		this.theme = theme;
	}

	/**
	 * Gets the field theme
	 * 
	 * @return The field theme
	 */

	public ITheme getTheme() {
		return theme;
	}

	/**
	 * INNER_CLASS <br>
	 * Represents the client validator context. This object can transmits
	 * messages to different validator elements.
	 * 
	 * @author Elian ORIOU
	 * 
	 */

	class ClientValidatorContextImpl implements IClientValidatorContext {

		private Stack<IMessage> messages;
		private ValidatorMessagesProvider messagesProvider = ValidatorMessagesProvider.INSTANCE;

		public ClientValidatorContextImpl() {
			this.messages = new Stack<IMessage>();
		}

		/**
		 * Add a specific message
		 */

		@Override
		public void addMessage(String errorCode, String value, Severity sev) {
			Message e = new Message(errorCode, value, sev);
			messages.push(e);
			manageMessage(e);
			showErrorFeedBack();
		}

		/**
		 * Add a global message
		 */

		@Override
		public void addGlobalMessage(String errorCode, String value,
				Severity sev) {
			Message e = new Message(errorCode, value, sev);
			messages.push(e);
			manageMessage(e);
			showErrorFeedBack();
		}

		/**
		 * Returns the last message
		 */

		@Override
		public IMessage getLastMessage() {
			return messages.peek();
		}

		/**
		 * Returns the messages provider object
		 */

		@Override
		public ValidatorMessagesProvider getMessagesProvider() {
			return messagesProvider;
		}
	}
}